package cn.dj.basic.service.impl;

import cn.dj.basic.dto.PhoneCodeDto;
import cn.dj.basic.exception.BusinessException;
import cn.dj.basic.service.IVerifyCodeService;
import cn.dj.basic.util.SmsUtils;
import cn.dj.basic.util.StrUtils;
import cn.dj.basic.util.VerifyCodeUtils;
import cn.dj.user.domain.User;
import cn.dj.user.mapper.UserMapper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service//创建业务类代理对象
public class VerifyCodeServiceImpl implements IVerifyCodeService {
    @Autowired//注入
    private RedisTemplate redisTemplate;//操作Redis数据库
    //注入
    @Autowired
    private UserMapper userMapper;//用于操作Redis数据库的对象

    @Override
    public String getImageCode(String key) {
        //1.获取图形验证码
        String code = StrUtils.getComplexRandomString(4);
        //2.保存到Redis（数据库中，缓存中）：key - 图形验证码code【并且设置5分钟过期】
       redisTemplate.opsForValue().set(key,code,5, TimeUnit.MINUTES);//存入数据库
        //3.将图形验证码写入图片并编码成base64的字符串，直接返回
        return VerifyCodeUtils.verifyCode(115,40,code);
    }

    @Override
    public void getPhoneCode(PhoneCodeDto dto) {
        //一：校验
        //1.校验空值：看前端传递过来的图形验证码和电话号码是否为空 - 是否填写
        if (StringUtils.isEmpty(dto.getImageCode())|| StringUtils.isEmpty(dto.getPhone())){
            throw new BusinessException("信息不能为空！");
        }
        //2.校验手机号码是否已经注册过 - 若根据手机号查找得到用户，表示已经注册过
        User user = userMapper.loadByPhone(dto.getPhone());//根据后端获取到前端传递过来的对象中的手机号，来查找是否有该用户 - 看手机号是否注册过
        if (user!= null){//非空，注册过
            throw new BusinessException("当前号码已经注册，请直接登录。如果忘记密码，请联系管理员!!");
        }
        //3.图形验证码是否过期【判断是否为null】，是否正确【要比对】
        Object obj = redisTemplate.opsForValue().get(dto.getImageCodeKey());//getImageCodeKey根据这个key（imageCodeKey）获取redis中存储的value值
        if (obj == null){//是否过期- 过期就没找到数据
            throw new BusinessException("图形验证码过期，请点击图形验证码进行重新获取");
        }
        //如果用户输入的验证码 和 redis数据库中的验证码不一致
                //equalsIgnoreCase比较时忽略大小写
        if (!dto.getImageCode().equalsIgnoreCase(obj.toString())){//是否正确 - 因为true才能进入代码块，所以取反-也就是验证码不一致进入抛异常
            throw new BusinessException("图形验证码错误");
        }

        //二：获取手机短信验证码：【存储格式key-value也就是->   业务键：手机号码 - 手机验证码:时间戳】
        //1.通过key从redis获取手机短信验证码
                //phoneCodeObj手机验证码的对象 - 即手机验证码:时间戳
                //【每次点击获取验证码，都会往redis数据库中添加/刷新一次数据(uuid是用户唯一标识，还是那个uuid)】
        Object phoneCodeObj = redisTemplate.opsForValue().get("register:" + dto.getPhone());//业务键：手机号码的格式
        //2.判断是否为null
        String phoneCode=null; //用于接收验证码
        if (phoneCodeObj == null){
            //2.1. 为空 = 第一次发送或者手机验证码过期了 => 重新生成一个
            phoneCode = StrUtils.getRandomString(4);//4位数的验证码
        }else{//用的没有过去那一个 - 反正保证有一个验证码
            //2.2. 不为空 = 手机验证码没有过期。判断是否过了重发时间
            long now = System.currentTimeMillis();
                //分割获取到手机发送时存入redis中的时间戳
            long old = Long.parseLong(phoneCodeObj.toString().split(":")[1]);//手机验证码:时间戳按照：进行分割，获取老时间戳
            if ((now - old)<1*60*1000){//1*60*1000——1min对应的ms是多少 - 1分钟可后重新获取。但存在redis的验证码是3分钟才过期
                //2.2.1.没有过重发时间 = 违规操作【抛业务异常】
                //还没有到下次重新发送的时间，就又要获取手机验证码，就抛异常
                throw  new BusinessException("请勿重复发送验证码！！");
            }else{
                //2.2.2.过了重发时间 = 可以重新发送【用没有过期的那个验证码】
                 /*重发后的验证码会又存入redis，所以在下面刷新过期时间[先获取请求发送过来，根据key找到value-phoneCodeObj的手机验证码-存在redis中的]
                 * 过了重发时间，但是数据库中是3分钟过期，还没过期，所以再次重发获取验证码，要将数据库中的已存有的手机验证码重新计算时长
                 * */
                phoneCode = phoneCodeObj.toString().split(":")[0];//分割获取到手机验证码
            }
        }
        //3.刷新过期时间 = 重新添加一次数据到redis
            //【以防刚刚再次拿到验证码1s就到过期时间了】
        redisTemplate.opsForValue().set("register:"+dto.getPhone()
                ,phoneCode+":"+System.currentTimeMillis(),3,TimeUnit.MINUTES);//3分钟过期时间

        //三：调用第三方接口发送短信验证码：调用工具类
//        SmsUtils.sendSms(dto.getPhone(),"你的手机验证码是："+phoneCode+",请在3分钟内使用！");
        System.out.println("你的手机验证码是："+phoneCode+",请在3分钟内使用！");
    }

    @Override
    public void getBinderSmsCode(PhoneCodeDto dto) {
        //一：校验
        //1.校验空值：看前端传递过来的图形验证码和电话号码是否为空 - 是否填写
        if (StringUtils.isEmpty(dto.getPhone())){
            throw new BusinessException("手机号不能为空！");
        }

        //二：获取手机短信验证码：【存储格式key-value也就是->   业务键：手机号码 - 手机验证码:时间戳】
        //1.通过key从redis获取手机短信验证码
        //phoneCodeObj手机验证码的对象 - 即手机验证码:时间戳
        //【每次点击获取验证码，都会往redis数据库中添加/刷新一次数据(uuid是用户唯一标识，还是那个uuid)】
        Object phoneCodeObj = redisTemplate.opsForValue().get("binder:" + dto.getPhone());//业务键：手机号码的格式
        //2.判断是否为null
        String phoneCode=null; //用于接收验证码
        if (phoneCodeObj == null){
            //2.1. 为空 = 第一次发送或者手机验证码过期了 => 重新生成一个
            phoneCode = StrUtils.getRandomString(4);//4位数的验证码
        }else{//用的没有过去那一个 - 反正保证有一个验证码
            //2.2. 不为空 = 手机验证码没有过期。判断是否过了重发时间
            long now = System.currentTimeMillis();
            //分割获取到手机发送时存入redis中的时间戳
            long old = Long.parseLong(phoneCodeObj.toString().split(":")[1]);//手机验证码:时间戳按照：进行分割，获取老时间戳
            if ((now - old)<1*60*1000){//1*60*1000——1min对应的ms是多少 - 1分钟可后重新获取。但存在redis的验证码是3分钟才过期
                //2.2.1.没有过重发时间 = 违规操作【抛业务异常】
                //还没有到下次重新发送的时间，就又要获取手机验证码，就抛异常
                throw  new BusinessException("请勿重复发送验证码！！");
            }else{
                //2.2.2.过了重发时间 = 可以重新发送【用没有过期的那个验证码】
                /*重发后的验证码会又存入redis，所以在下面刷新过期时间[先获取请求发送过来，根据key找到value-phoneCodeObj的手机验证码-存在redis中的]
                 * 过了重发时间，但是数据库中是3分钟过期，还没过期，所以再次重发获取验证码，要将数据库中的已存有的手机验证码重新计算时长
                 * */
                phoneCode = phoneCodeObj.toString().split(":")[0];//分割获取到手机验证码
            }
        }
        //3.刷新过期时间 = 重新添加一次数据到redis
        //【以防刚刚再次拿到验证码1s就到过期时间了】
        redisTemplate.opsForValue().set("binder:"+dto.getPhone()
                ,phoneCode+":"+System.currentTimeMillis(),3,TimeUnit.MINUTES);//3分钟过期时间

        //三：调用第三方接口发送短信验证码：调用工具类
//        SmsUtils.sendSms(dto.getPhone(),"你的手机验证码是："+phoneCode+",请在3分钟内使用！");
        System.out.println("你的手机验证码是："+phoneCode+",请在3分钟内使用！");
    }
}
